package cn.assumejson.springframework.beans.factory.config;

import cn.assumejson.springframework.beans.PropertyValue;
import cn.assumejson.springframework.beans.PropertyValues;
import cn.assumejson.springframework.core.io.DefaultResourceLoader;
import cn.assumejson.springframework.core.io.Resource;
import cn.assumejson.springframework.core.util.StringValueResolver;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * 处理占位符的问题
 *
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/12/2 22:02
 */
@Slf4j
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {

    /**
     * Default placeholder prefix: {@value}
     */
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

    /**
     * Default placeholder suffix: {@value}
     */
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

    private String location;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {

        List<Properties> propertiesList = new LinkedList<>();
        try {
            DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
            // 加载配置文件
            if (StrUtil.isBlank(location)) {
                // 获取classPath 下所有的文件
                File file = new File(getClassPath());
                String[] list = file.list();
                Assert.notNull(list, "this list is must be not null !");
                for (String fileName : list) {
                    if (fileName.contains(".properties")) {
                        this.location = "classpath:" + fileName;
                        Properties properties = reLoad(beanFactory, resourceLoader);
                        propertiesList.add(properties);
                    }
                }
            } else {
                Properties properties = reLoad(beanFactory, resourceLoader);
                propertiesList.add(properties);
            }

            // 向容器中添加字符串解析器，供解析@Value注解使用
            StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(propertiesList);
            beanFactory.addEmbeddedValueResolver(valueResolver);
        } catch (Exception e) {
            log.error("PropertyPlaceholderConfigurer.postProcessBeanFactory is fail");
        }
    }

    private String getClassPath() {
        return Objects.requireNonNull(this.getClass().getResource("")).getPath().split("classes")[0] + "classes";
    }

    private Properties reLoad(ConfigurableListableBeanFactory beanFactory, DefaultResourceLoader resourceLoader) throws IOException {
        Resource resource = resourceLoader.getResource(location);
        Properties properties = new Properties();

        properties.load(resource.getInputStream());

        // 获取所有的BeanDefinitionNames
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();

        for (String beanName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                Object value = propertyValue.getValue();
                if (!(value instanceof String)) continue;
                value = resolvePlaceholder((String) value, properties);
                propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), value));
            }
        }
        return properties;
    }

    private String resolvePlaceholder(String value, List<Properties> properties) {
        String result = null;
        for (Properties property : properties) {
            result = resolvePlaceholder(value, property);
            if (!value.equals(result)) return result;
        }
        return result;
    }

    private String resolvePlaceholder(String value, Properties properties) {
        StringBuilder buffer = new StringBuilder(value);
        int startIdx = value.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
        int stopIdx = value.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
        if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {
            String propKey = value.substring(startIdx + 2, stopIdx);
            if (properties.containsKey(propKey)) {
                String propVal = properties.getProperty(propKey);
                buffer.replace(startIdx, stopIdx + 1, propVal);
            }
        }
        return buffer.toString();
    }

    public void setLocation(String location) {
        this.location = location;
    }

    private class PlaceholderResolvingStringValueResolver implements StringValueResolver {
        private List<Properties> propertiesList = new ArrayList<>();

        public PlaceholderResolvingStringValueResolver(Properties properties) {
            propertiesList.add(properties);
        }

        public PlaceholderResolvingStringValueResolver(List<Properties> propertiesList) {
            this.propertiesList = propertiesList;
        }

        @Override
        public String resolveStringValue(String strVal) {
            return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, propertiesList);
        }
    }
}
